#pragma once

#include "net/instaweb/htmlparse/public/html_node.h"
#include "net/instaweb/htmlparse/public/html_element.h"
#include "net/instaweb/htmlparse/public/html_parse.h"
using namespace net_instaweb;

#include "Config.h"

class CHtmlElement;

class CHtmlNode 
{  
protected:
  HtmlParse *m_parse;
  HtmlNode *m_node;

  CHtmlNode(HtmlParse *parse, HtmlNode *node) 
    : m_parse(parse), m_node(node) 
  {

  }
public:
  static void Expose(void);

  HtmlParse *GetParse(void) const { return m_parse; }  

  py::object GetParent(void) const;

  bool IsAlive(void) const { return m_node->live(); }

  int Compare(py::object other);
};

class CHtmlLeafNode : public CHtmlNode
{
protected:
  CHtmlLeafNode(HtmlParse *parse, HtmlLeafNode *node) 
    : CHtmlNode(parse, node)
  {

  }
};

class CHtmlCdataNode : public CHtmlLeafNode
{
public:
  CHtmlCdataNode(HtmlParse *parse, HtmlCdataNode *node) 
    : CHtmlLeafNode(parse, node)
  {

  }

  const std::string GetContents(void) const { return static_cast<HtmlCdataNode *>(m_node)->contents(); }
};

class CHtmlCharactersNode : public CHtmlLeafNode
{
public:
  CHtmlCharactersNode(HtmlParse *parse, HtmlCharactersNode *node) 
    : CHtmlLeafNode(parse, node)
  {

  }

  const std::string GetContents(void) const { return static_cast<HtmlCharactersNode *>(m_node)->contents(); }

  void Append(const std::string& str) { static_cast<HtmlCharactersNode *>(m_node)->Append(str); }
};

class CHtmlCommentNode : public CHtmlLeafNode
{
public:
  CHtmlCommentNode(HtmlParse *parse, HtmlCommentNode *node) 
    : CHtmlLeafNode(parse, node)
  {

  }

  const std::string GetContents(void) const { return static_cast<HtmlCommentNode *>(m_node)->contents(); }
};

class CHtmlIEDirectiveNode : public CHtmlLeafNode
{
public:
  CHtmlIEDirectiveNode(HtmlParse *parse, HtmlIEDirectiveNode *node) 
    : CHtmlLeafNode(parse, node)
  {

  }

  const std::string GetContents(void) const { return static_cast<HtmlIEDirectiveNode *>(m_node)->contents(); }
};

class CHtmlDirectiveNode : public CHtmlLeafNode
{
public:
  CHtmlDirectiveNode(HtmlParse *parse, HtmlDirectiveNode *node) 
    : CHtmlLeafNode(parse, node)
  {

  }

  const std::string GetContents(void) const { return static_cast<HtmlDirectiveNode *>(m_node)->contents(); }
};

class CHtmlAttribute 
{
  CHtmlElement& m_element;
  HtmlElement::Attribute& m_attr;
public:
  CHtmlAttribute(CHtmlElement& element, HtmlElement::Attribute& attr)
    : m_element(element), m_attr(attr)
  {

  }

  int Compare(py::object other);

  py::object GetParent(void) const;
  const HtmlElement::Attribute& GetAttribute(void) const { return m_attr; }

  const std::string GetName(void) const { return m_attr.name().c_str(); }
  void SetName(const std::string& name);

  const std::string GetValue(void) const { return m_attr.value(); }
  void SetValue(const std::string& value) { m_attr.SetValue(value); }
  const std::string GetEscapedValue(void) const { return m_attr.escaped_value(); }
  void SetEscapedValue(const std::string& value) { m_attr.SetEscapedValue(value); }
  const std::string GetQuote(void) const { return m_attr.quote(); }
  void SetQuote(const std::string& quote) { m_attr.set_quote(quote.c_str()); }
};

class CHtmlElement : public CHtmlNode
{
  const std::string ToString(py::object obj);

  void GuessKey(py::object key, int& index, std::string& name);
public:
  CHtmlElement(HtmlParse *parse, HtmlElement *node) 
    : CHtmlNode(parse, node)
  {

  }

  const std::string GetTagName(void) const { return static_cast<HtmlElement *>(m_node)->tag().c_str(); }
  void SetTagName(const std::string& name) { static_cast<HtmlElement *>(m_node)->set_tag(m_parse->Intern(name)); }

  HtmlElement::CloseStyle GetCloseStyle(void) const { return static_cast<HtmlElement *>(m_node)->close_style(); }
  void SetCloseStyle(HtmlElement::CloseStyle style) { static_cast<HtmlElement *>(m_node)->set_close_style(style); }

  int GetBeginLineNumber(void) const { return static_cast<HtmlElement *>(m_node)->begin_line_number(); }
  int GetEndLineNumber(void) const { return static_cast<HtmlElement *>(m_node)->end_line_number(); }

  void AddAttribute(const CHtmlAttribute& attr)
  {
    static_cast<HtmlElement *>(m_node)->AddAttribute(attr.GetAttribute());
  }
  void AddAttribute(const std::string& name, const std::string& value, const std::string quote)
  {
    static_cast<HtmlElement *>(m_node)->AddAttribute(m_parse->Intern(name), value, quote.empty() ? NULL : quote.c_str());
  }
  void AddAttribute(const std::string& name, int value)
  {
    static_cast<HtmlElement *>(m_node)->AddAttribute(m_parse->Intern(name), value);
  }
  void AddEscapedAttribute(const std::string& name, const std::string& value, const std::string quote)
  {
    static_cast<HtmlElement *>(m_node)->AddEscapedAttribute(m_parse->Intern(name), value, quote.empty() ? NULL : quote.c_str());
  }

  void DeleteAttribute(int index) { static_cast<HtmlElement *>(m_node)->DeleteAttribute(index); }
  void DeleteAttribute(const std::string& name) { static_cast<HtmlElement *>(m_node)->DeleteAttribute(m_parse->Intern(name)); }

  py::object FindAttribute(const std::string& name);
  py::object GetAttributeValue(const std::string& name);

  int GetAttributeSize(void) const { return static_cast<HtmlElement *>(m_node)->attribute_size(); }
  py::object GetAttribute(int index);

  py::object GetItem(py::object key);
  void SetItem(py::object key, py::object value);
  void DelItem(py::object key);
  bool Contains(py::object key);
};
